package com.culturalCenter.dataCenter.globalconfig.filter;


import com.culturalCenter.dataCenter.Utils.*;
import com.culturalCenter.dataCenter.customEntity.CustomApiResult;
import com.culturalCenter.dataCenter.customEntity.CustomToken;
import com.culturalCenter.dataCenter.mapper.sysauth.SystemAuthMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.client.RestTemplate;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 验证所有请求
 *
 * @author Rockylee
 */
@Slf4j
public class AuthenticateFilter extends BasicAuthenticationFilter {

    private Collection<? extends GrantedAuthority> authorities;
    private String tokenHeader;

    private String head;

    private UserDetailsService userDetailsService;

    private List<String> permitRegexUris;

    private RedisTemplate<String, String> redisTemplate;

    private String userAsciiKey;

    private Boolean checkToken;

    private RestTemplate restTemplate;

    private SystemAuthMapper systemAuthMapper;

    private String tokenHostUrl;

    private String checkTokenUrl;

    public AuthenticateFilter(AuthenticationManager authenticationManager, String tokenHeader, String head,
                              UserDetailsService userDetailsService, String permitUris, String userAsciiKey,
                              RedisTemplate<String, String> redisTemplate, Boolean checkToken,
                              String tokenHostUrl, String checkTokenUrl, RestTemplate restTemplate, SystemAuthMapper systemAuthMapper) {
        super(authenticationManager);
        this.head = head;
        this.tokenHeader = tokenHeader;
        this.userDetailsService = userDetailsService;
        this.redisTemplate = redisTemplate;
        this.userAsciiKey = userAsciiKey;
        this.checkToken = checkToken;
        this.tokenHostUrl = tokenHostUrl;
        this.checkTokenUrl = checkTokenUrl;
        this.restTemplate = restTemplate;
        this.systemAuthMapper = systemAuthMapper;
        this.permitRegexUris = Arrays.asList(permitUris.split(",")).stream().map(PathUtil::getRegPath).collect(Collectors.toList());
    }


    /**
     * 判断请求是否是否带有token信息，token是否合法，是否过期。设置安全上下文。
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 验证头部信息
        /*boolean validatePass = validateHeaderAuth(request);
        if (!validatePass) {
            RelstResponse(response, ApiResult.instance(ResultCode.HEAD_NEEDED));
            return;
        }*/

        String token = request.getHeader(tokenHeader);
        // 可能是登录或者注册的请求，不带token信息，又或者是不需要登录，不需要token即可访问的资源。
        String uri = request.getRequestURI();
        for (String regexPath : permitRegexUris) {
            // 存的路径则放行
            if (Pattern.matches(regexPath, uri)) {
                chain.doFilter(request, response);
                return;
            }
        }
        Claims claims;
        String userName;
        if (token == null) {
            log.warn("请登录访问");
            RelstResponse(response, ApiResult.instance(ResultCode.TOKEN_NEEDED));
            return;
            // throw new NoneTokenException(AuthErrorEnum.TOKEN_NEEDED.getMessage());
        }
        boolean isMobile = FrameUtils.isMobileDevice(request);

        String url = tokenHostUrl + checkTokenUrl;

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        String _token = token.substring(head.length()).trim();

        CustomToken customToken = new CustomToken();
        customToken.setToken(_token);
        customToken.setByMobile(isMobile);

        HttpEntity<CustomToken> httpEntity = new HttpEntity<>(customToken, headers);

        ResponseEntity<CustomApiResult> results = restTemplate.postForEntity(url, httpEntity, CustomApiResult.class);
        if (results.getStatusCode() != HttpStatus.OK) {
            return;
        }
        CustomApiResult body = results.getBody();
        if (!body.getSuccess()) {
            RelstResponse(response, ApiResult.instance(ResultCode.AUTH_HEADER_ERROR));
            return;
        }

        claims = JwtTokenUtils.getClaimsFromToken(token.substring(head.length()));
        userName = (String) claims.get("username");

        Map<String, List<String>> roles = (Map<String, List<String>>) claims.get("role");

        authorities = getAuthorities(roles.get("role"));
        // 生成生物认证
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userName, null, authorities);
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        log.info("用户：{}，正在访问：{}", userName, request.getRequestURI());
        log.info("authenticated user " + userName + ", setting security context");
        SecurityContextHolder.getContext().setAuthentication(authentication);

        //        if (!token.startsWith(head)) {
        //            log.warn("token信息不合法");
        //            RelstResponse(response, ApiResult.instance(ResultCode.AUTH_HEADER_ERROR));
        //            return;
        ////            throw new IllegalTokenAuthenticationException(AuthErrorEnum.AUTH_HEADER_ERROR.getMessage());
        //        }
        //        Claims claims = JwtTokenUtils.getClaimsFromToken(token.substring(head.length()));
        //        if (claims == null) {
        //            log.warn("token不存在");
        //            RelstResponse(response, ApiResult.instance(ResultCode.TOKEN_NEEDED));
        //            return;
        ////            throw new TokenIsExpiredException(AuthErrorEnum.TOKEN_EXPIRED.getMessage());
        //        }
        //        String userName = (String) claims.get("username");
        //        if (userName == null) {
        //            log.warn("userName不存在");
        //            RelstResponse(response, ApiResult.instance(ResultCode.LOGIN_NAME_ERROR));
        //            return;
        ////            throw new TokenIsExpiredException(AuthErrorEnum.TOKEN_EXPIRED.getMessage());
        //        }
        //
        //        Date expiredTime = claims.getExpiration();
        //        if ((new Date().getTime() > expiredTime.getTime())) {
        //            log.warn("当前token信息已过期,请重新登录");
        //            RelstResponse(response, ApiResult.instance(ResultCode.TOKEN_EXPIRED));
        //            return;
        ////            throw new TokenIsExpiredException(AuthErrorEnum.TOKEN_EXPIRED.getMessage());
        //        }
        //
        //        //获取保存token
        ////        int userAscii = CharAscii.SumStrAscii(userName);
        //        RedisUtil ru = new RedisUtil(redisTemplate);
        //        String storeToken = ru.get(userName);
        //        if (!storeToken.equals(token.substring(head.length()))) {
        //            log.warn("当前token信息被重新登录了");
        //            RelstResponse(response, ApiResult.instance(ResultCode.ALREADY_LOGIN));
        //            return;
        //        }
        //        Map<String, List<String>> roles = (Map<String, List<String>>) claims.get("role");
        //
        //        authorities = getAuthorities(roles.get("role"));
        //        //生成生物认证
        //        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userName, null, authorities);
        //        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        //        log.info("用户：{}，正在访问：{}", userName, request.getRequestURI());
        //        log.info("authenticated user " + userName + ", setting security context");
        //        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(request, response);
    }

    private void RelstResponse(HttpServletResponse response, ApiResult result) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter out = response.getWriter();
        response.setStatus(200);
        ObjectMapper om = new ObjectMapper();
        out.write(om.writeValueAsString(result));
        out.flush();
        out.close();
    }

    public Collection<? extends GrantedAuthority> getAuthorities(List<String> data) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String role : data) {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        return authorities;
    }

    /**
     * 校验请求头 acc_xx
     */
    /* private boolean validateHeaderAuth(HttpServletRequest request) {
        try {
            String appId = request.getHeader(AuthConstants.ACC_APPID);
            String appKey = request.getHeader(AuthConstants.ACC_APPKEY);
            String timestamp = request.getHeader(AuthConstants.ACC_TIMES);
            String token = request.getHeader(AuthConstants.ACC_TOKEN);
            long currentTimeMillis = System.currentTimeMillis();
            long times = AuthConstants.REQUEST_TTL;
            if (Objects.isNull(timestamp) || (currentTimeMillis - Long.parseLong(timestamp) > times)) {
                // 请求超时
                return false;
            }

            SystemAuth systemAuth = systemAuthMapper.queryByAppId(appId);
            if (Objects.isNull(systemAuth)) {
                // 此appid不存在系统中，直接返回验证失败
                log.warn("appid: {} 不存在在系统中", appId);
                return false;
            }
            String md5Hex = DigestUtil.md5Hex(systemAuth.getAppkey() + systemAuth.getAppsecret() + timestamp).toUpperCase();
            if (!Objects.equals(token, md5Hex)) {
                log.warn("token校验失败,参数：appid:{},appKey:{},timestamp:{},token:{}", appId, appKey, timestamp, token);
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
    }*/
}
